package net.njcp.mybatis.frame.service.impl;

import net.njcp.mybatis.frame.builder.CustomSqlBuilder;
import net.njcp.mybatis.frame.dao.BaseMapper;
import net.njcp.mybatis.frame.service.BaseService;
import net.njcp.mybatis.frame.utils.MapUtil;
import net.njcp.mybatis.frame.utils.ReflectUtil;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @ClassName BaseServiceImpl
 * @Description: TODO
 * @Author 柳拓
 * @Date 2019/11/6
 * @Version V1.0
 **/
public class BaseServiceImpl implements BaseService{

    private Logger logger = LoggerFactory.getLogger(BaseServiceImpl.class);

    private  SqlSessionFactory sessionFactory;

    private BaseServiceImpl(){}

    private static class InstanceBuilder{
        private static BaseServiceImpl baseService = new BaseServiceImpl();
    }

    public static BaseServiceImpl getInstance(){
        return InstanceBuilder.baseService;
    }

    public void init(){
        Configuration configuration = sessionFactory.getConfiguration();
        configuration.addMapper(BaseMapper.class);
//        //通过这种方式打开的SqlSession，autoCommit默认为false，需要手动提交事务
//        SqlSession session = sessionFactory.openSession(true);
    }

    @Override
    public void add(Object o) {
        SqlSession session = sessionFactory.openSession(true);
        try {
            BaseMapper baseMapper = session.getMapper(BaseMapper.class);
            baseMapper.executeInsertSql(o);
        }catch (Exception e){
            e.printStackTrace();
            logger.error(e.toString(),e);
        }finally {
            session.close();
        }
    }

    @Override
    public void deleteById(Class c, Serializable id) {
        SqlSession session = sessionFactory.openSession(true);
        try {
            BaseMapper baseMapper = session.getMapper(BaseMapper.class);
            if (id instanceof Comparable){
                CustomSqlBuilder customSqlBuilder = CustomSqlBuilder.build().delete(c).where(ReflectUtil.getPrimaryKey(c)+" = "+id);
                baseMapper.executeBuildSql(customSqlBuilder);
            } else {
                try {
                    delete(c, MapUtil.Obj2Map(id));
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error(e.toString(),e);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
            logger.error(e.toString(),e);
        }finally {
            session.close();
        }
    }

    @Override
    public void delete(Class c, Map<String,Object> condition) {
        SqlSession session = sessionFactory.openSession(true);
        try {
            BaseMapper baseMapper = session.getMapper(BaseMapper.class);
            CustomSqlBuilder customSqlBuilder = CustomSqlBuilder.build().delete(c);
            try {
                int i=0;
                condition.entrySet().stream().forEach(entry->{
                    String value;
                    if (entry.getValue() instanceof String){
                        value = "'"+entry.getValue()+"'";
                    }else if(entry.getValue() instanceof Date){
                        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        value = "'"+formatter.format(entry.getValue())+"'";
                    }else {
                        value = String.valueOf(entry.getValue());
                    }
                    if (i==0){
                        customSqlBuilder.where(entry.getKey()+" = "+value);
                    }else{
                        customSqlBuilder.and(entry.getKey()+" = "+value);
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
                logger.error(e.toString(),e);
            }
            baseMapper.executeBuildSql(customSqlBuilder);
        }catch (Exception e){
            e.printStackTrace();
            logger.error(e.toString(),e);
        }finally {
            session.close();
        }
    }

    @Override
    public void update(Object o) {
        SqlSession session = sessionFactory.openSession(true);
        try {
            BaseMapper baseMapper = session.getMapper(BaseMapper.class);
            baseMapper.executeUpdateSql(o);
        }catch (Exception e){
            e.printStackTrace();
            logger.error(e.toString(),e);
        }finally {
            session.close();
        }
    }

    @Override
    public void updateBy(Object o, String fieldName, Object fieldValue) {
        SqlSession session = sessionFactory.openSession(true);
        try {
            BaseMapper baseMapper = session.getMapper(BaseMapper.class);
            CustomSqlBuilder customSqlBuilder = CustomSqlBuilder.build().where(fieldName+"="+fieldName);
            baseMapper.executeBuildSql(customSqlBuilder);
        }catch (Exception e){
            e.printStackTrace();
            logger.error(e.toString(),e);
        }finally {
            session.close();
        }
    }

    @Override
    public <T> T findByPrimaryKey(Class<T> c, Serializable id) {
        SqlSession session = sessionFactory.openSession(true);
        try {
            BaseMapper baseMapper = session.getMapper(BaseMapper.class);
            CustomSqlBuilder customSqlBuilder = CustomSqlBuilder.build();
            if(id instanceof Comparable){
                customSqlBuilder.select(c).where(ReflectUtil.getPrimaryKey(c)+" = "+id);
            }else{
                customSqlBuilder.select(c);
                try {
                    Map<String, Object> keyMap = MapUtil.Obj2Map(id);
                    keyMap.entrySet().forEach(e -> {
                        String fieldName = e.getKey();
                        String fieldValue = null;
                        if (e.getValue() instanceof String){
                            fieldValue = "'"+e.getValue()+"'";
                        }else if(e.getValue() instanceof Date){
                            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                            fieldValue = "'"+formatter.format(e.getValue())+"'";
                        }else{
                            fieldValue = String.valueOf(e.getValue());
                        }
                        customSqlBuilder.where(fieldName+" = "+fieldValue);
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error(e.toString(),e);
                }
            }
            List<Map<String, Object>> mapResult = baseMapper.executeBuildSql(customSqlBuilder);
            if (!mapResult.isEmpty()){
                try {
                    return MapUtil.map2Obj(mapResult.get(0), c);
                } catch (Exception e) {
                    logger.info(e.getMessage()+" MapUtil convert exception!");
                    return null;
                }
            }
            return null;
        }catch (Exception e){
            e.printStackTrace();
            logger.error(e.toString(),e);
            return null;
        }finally {
            session.close();
        }
    }

    @Override
    public <T> List<T> findAll(Class<T> c) {
        SqlSession session = sessionFactory.openSession(true);
        try {
            BaseMapper baseMapper = session.getMapper(BaseMapper.class);
            CustomSqlBuilder customSqlBuilder = CustomSqlBuilder.build();
            customSqlBuilder.select(c);
            List<Map<String, Object>> mapResult = baseMapper.executeBuildSql(customSqlBuilder);
            List<T> result = new ArrayList<>();
            mapResult.stream().forEach(
                    map->{
                        try {
                            result.add((T) MapUtil.map2Obj(map, c));
                        } catch (Exception e) {
                            throw new RuntimeException(e.getMessage());
                        }
                    }
            );
            return result;
        }catch (Exception e){
            e.printStackTrace();
            logger.error(e.toString(),e);
            return new ArrayList<>();
        }finally {
            session.close();
        }
    }

    /**
     * 根据特定查询条件查询，固定字段
     * @param c 表对象
     * @param columns 多个字段
     * @param fieldName 判断字段
     * @param operator 运算符
     * @param fieldValue 条件值
     * @param <T>
     * @return
     */
    @Override
    public <T> List<T> findBy(Class<T> c, String fieldName, String operator, Object fieldValue, String... columns) {
        SqlSession session = sessionFactory.openSession(true);
        try {
            BaseMapper baseMapper = session.getMapper(BaseMapper.class);
            CustomSqlBuilder customSqlBuilder = CustomSqlBuilder.build();
            if (columns != null){
                customSqlBuilder.select(c,columns);
            } else {
                customSqlBuilder.select(c);
            }
            if (fieldName != null){
                customSqlBuilder.where(fieldName+operator+fieldValue);
            }
            List<Map<String, Object>> mapResult = baseMapper.executeBuildSql(customSqlBuilder);
            List<T> result = new ArrayList<>();
            mapResult.stream().forEach(
                    map->{
                        try {
                            result.add((T) MapUtil.map2Obj(map, c));
                        } catch (Exception e) {
                            throw new RuntimeException(e.getMessage());
                        }
                    }
            );
            return result;
        }catch (Exception e){
            e.printStackTrace();
            logger.error(e.toString(),e);
            return new ArrayList<>();
        }finally {
            session.close();
        }
    }

    @Override
    public <T> List<T> findBy(Class<T> c, String fieldName, Object fieldValue) {
        try {
            return findBy(c, fieldName,"=",fieldValue,null);
        }catch (Exception e){
            e.printStackTrace();
            logger.error(e.toString(),e);
            return new ArrayList<>();
        }
    }

    @Override
    public <T> List<T> findContent(Class<T> c, String... columns) {
        try {
            return findBy(c, null,null,null, columns);
        }catch (Exception e){
            e.printStackTrace();
            logger.error(e.toString(),e);
            return new ArrayList<>();
        }
    }


    @Override
    public <T> List<T> findContentBy(Class<T> c, String fieldName, Object fieldValue, String... content) {
        try {
            return findBy(c, fieldName,"=",fieldValue,content);
        }catch (Exception e){
            e.printStackTrace();
            logger.error(e.toString(),e);
            return new ArrayList<>();
        }
    }

    @Override
    public <T> List<T> executeBuildSql(CustomSqlBuilder builder) {
        SqlSession session = sessionFactory.openSession(true);
        List<Map<String, Object>> mapResult = null;
        try {
            BaseMapper baseMapper = session.getMapper(BaseMapper.class);
            mapResult = baseMapper.executeBuildSql(builder);
        }catch (Exception e){
            logger.error(e.toString(),e);
        }finally {
            session.close();
        }
        List<T> result = new ArrayList<>();
        if (builder.gettClass() != null&&mapResult!=null){
            mapResult.stream().forEach(
                    map->{
                        try {
                            result.add((T) MapUtil.map2Obj(map, builder.gettClass()));
                        } catch (Exception e) {
                            e.printStackTrace();
                            logger.error(e.toString(),e);
                        }
                    }
            );
        }
        return result;
    }

    @Override
    public <T> List<T> executeBuildSqlGetField(CustomSqlBuilder builder) {
        SqlSession session = sessionFactory.openSession(true);
        session.clearCache();
        List<Map<String, Object>> mapResult = null;
        try {
            BaseMapper baseMapper = session.getMapper(BaseMapper.class);
            mapResult = baseMapper.executeBuildSql(builder);
        }catch (Exception e){
            logger.error(e.toString(),e);
        }finally {
            session.close();
        }

        List<T> result = new ArrayList<>();
        if(mapResult!=null) {
            mapResult.forEach(r -> {
                result.addAll((Collection<? extends T>) r.values());
            });
        }
        return result;
    }

    public List<Map<String, Object>> executeSql(String sql){
        SqlSession session = sessionFactory.openSession(true);
        try {
            BaseMapper baseMapper = session.getMapper(BaseMapper.class);
            return baseMapper.executeSql(sql);
        }catch (Exception e){
            e.printStackTrace();
            logger.error(e.toString(),e);
            return null;
        }finally {
            session.close();
        }
    }

    public SqlSessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public void setSessionFactory(SqlSessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
}
